/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./BezierSurface.h"

/**************************************************************
* Constructors                                                *
***************************************************************/
BezierSurface::BezierSurface(): Mesh() {
	mVertexTable = cNull;
	mEdgeTable = cNull;
	CalculateBasis();
	CalculateSurface();
	mSurfacesVisible = true;
}

BezierSurface::~BezierSurface() {
	for (cInt i = 0; i < mHorizontalCurves.GetSize(); i++) {
		delete mHorizontalCurves[i];
	}
	for (cInt i = 0; i < mVerticalCurves.GetSize(); i++) {
		delete mVerticalCurves[i];
	}
}
/**************************************************************
* Operators                                                   *
***************************************************************/


/**************************************************************
* Other helpful functions                                     *
***************************************************************/
/*virtual*/ cVoid BezierSurface::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane) {
	if (mSurfacesVisible) {
		for (cInt i = 0; i < mHorizontalCurves.GetSize(); i++) {
			mHorizontalCurves[i]->Render(renderer, matrix, plane, mSurfaceColor);
		}
		for (cInt i = 0; i < mVerticalCurves.GetSize(); i++) {
			mVerticalCurves[i]->Render(renderer, matrix, plane, mSurfaceColor);
		}
	}
	Mesh::Render(renderer, matrix, plane);
}

/*virtual*/ cVoid BezierSurface::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane, cWinColor color) {
	if (mSurfacesVisible) {
		for (cInt i = 0; i < mHorizontalCurves.GetSize(); i++) {
			mHorizontalCurves[i]->Render(renderer, matrix, plane, color);
		}
		for (cInt i = 0; i < mVerticalCurves.GetSize(); i++) {
			mVerticalCurves[i]->Render(renderer, matrix, plane, color);
		}
	}
	Mesh::Render(renderer, matrix, plane, color);
}

/*virtual*/ cVoid BezierSurface::UpdateMeshPoint(cInt iRow, cInt iCol, Point4D point) {
	Mesh::UpdateMeshPoint(iRow, iCol, point);
	CalculateSurface();
}

cVoid BezierSurface::CreateSurface(AbstractCurve* curve1, AbstractCurve* curve2) {
	Mesh::Create(curve1, curve2);
	CalculateSurface();
}

cVoid BezierSurface::CalculateBasis() {
	mHorizontalBasis = BasisList(mU + 1);
	mVerticalBasis = BasisList(mV + 1);
	cFloat dt = 1.0f / mU, t;
	cFloat b10, b20, b21, b22;
	t = 0.0f;

	for (cInt i = 0; i < mU + 1; i++, t += dt) {
		b10 = 1.0f - t;
		b20 = b10 * b10;
		b21 = 2.0f * t * b10;
		b22 = t * t;
		mHorizontalBasis[i].mValues[0] = b10 * b20;
		mHorizontalBasis[i].mValues[1] = t * b20 + b10 * b21;
		mHorizontalBasis[i].mValues[2] = t * b21 + b10 * b22;
		mHorizontalBasis[i].mValues[3] = t * b22;
	}	
	dt = 1.0f / mV;
	t = 0.0f;
	for (cInt i = 0; i < mV + 1; i++, t += dt) {
		b10 = 1.0f - t;
		b20 = b10 * b10;
		b21 = 2.0f * t * b10;
		b22 = t * t;
		mVerticalBasis[i].mValues[0] = b10 * b20;
		mVerticalBasis[i].mValues[1] = t * b20 + b10 * b21;
		mVerticalBasis[i].mValues[2] = t * b21 + b10 * b22;
		mVerticalBasis[i].mValues[3] = t * b22;
	}
}

cVoid BezierSurface::CalculateSurface() {
	for (cInt i = 0; i < mHorizontalCurves.GetSize(); i++) {
		delete mHorizontalCurves[i];
	}
	mHorizontalCurves.Clear();
	for (cInt i = 0; i < mVerticalCurves.GetSize(); i++) {
		delete mVerticalCurves[i];
	}
	mVerticalCurves.Clear();

	for (cInt i = 0; i < mMesh.GetRowSize() - 3; i += 3) {
		for (cInt j = 0; j < mMesh.GetColSize() - 3; j += 3) {
			CalculateSurface(i, j);
		}
	}
}

cVoid BezierSurface::CalculateSurface(cInt hor, cInt vert) {
	Point4D p;
	BezierCurve *b;

	for (cInt i = 0; i < mU + 1; i++)	{
		b = new BezierCurve(false, false, mMeshColor);
		for (cInt k = 0; k < 4; k++) {
			p = Point4D();
			for (cInt j = 0; j < 4; j++) {
				p = p + mMesh(k + hor, j + vert).mPoint * mHorizontalBasis[i].mValues[j];
				p.mW = 1.0f;
			}
			b->AddPoint(p, 0);
		}
		mHorizontalCurves.Add(b);
	}
	for (cInt i = 0; i < mV + 1; i++) {
		b = new BezierCurve(false, false, mMeshColor);
		for (cInt k = 0; k < 4; k++) {
			p = Point4D();
			for (cInt j = 0; j < 4; j++) {
				p = p + mMesh(j + hor, k + vert).mPoint * mVerticalBasis[i].mValues[j];
				p.mW = 1.0f;
			}
			b->AddPoint(p);
		}
		mVerticalCurves.Add(b);
	}
}

/*virtual*/ cVoid BezierSurface::SwitchPeriodic() {
	Mesh::SwitchPeriodic();
	CalculateBasis();
	CalculateSurface();
}

/*virtual*/ cVoid BezierSurface::SetPeriodic(cBool bPeriodic) {
	if (bPeriodic != mIsPeriodic) {
		Mesh::SetPeriodic(bPeriodic);
		CalculateBasis();
		CalculateSurface();
	}
}

cVoid BezierSurface::ChangeU(cInt iU) {
	Mesh::ChangeU(iU);
	CalculateBasis();
	CalculateSurface();
}

cVoid BezierSurface::ChangeV(cInt iV) {
	Mesh::ChangeV(iV);
	CalculateBasis();
	CalculateSurface();
}

cVoid BezierSurface::SwitchSurfaceVisibility() {
	mSurfacesVisible = !mSurfacesVisible;
}


/*virtual*/ cChar* BezierSurface::GetName() {
	return "BezierSurface";
}

/*virtual*/ cVoid BezierSurface::DisplayInfo(Renderer& renderer) {
	cChar pcBuff [MAX_BUF_SIZE];
	cInt iLineNr = 0;

	sprintf_s(pcBuff, "Bezier Surface Info:");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, mSurfaceColor);
	sprintf_s(pcBuff, "U : ");	
	renderer.DrawString(H_TXT_SPACER, iLineNr * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "%3i", mU);	
	renderer.DrawString(H_TXT_SPACER * 3, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "V : ");	
	renderer.DrawString(H_TXT_SPACER, iLineNr * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "%3i", mV);	
	renderer.DrawString(H_TXT_SPACER * 3, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, Color::ToWinColor(WHITE));
	iLineNr++;
	sprintf_s(pcBuff, "[F1] - Hit After Defining First Curve");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[F2] - Hit After Defining Second Curve");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl/Alt + J] - Increase/Decrease U");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl/Alt + K] - Increase/Decrease V");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + N] - Show/Hide Control Points Coordinates");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + B] - Show/Hide Control Points");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + V] - Show/Hide Mesh");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Ctrl + C] - Show/Hide Surface");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "[Space] - Deselect Points");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
		sprintf_s(pcBuff, "[LMB & Drag] - Move Selected Points");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
}

/*virtual*/ TiXmlElement* BezierSurface::Serialize(TiXmlElement* parent) {
	TiXmlElement* elem = new TiXmlElement("Object3D");
	parent->LinkEndChild(elem);
	elem->SetAttribute("type", GetName());
	Mesh::Serialize(elem);

	return elem;
}